package msgcomm;

import java.io.Serializable;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

/**
 * Vector clock, non ha bisogno di presentazioni
 */
public class VectorClock implements Serializable, Cloneable {

	private static final long serialVersionUID = 756798323261583815L;
	/**
	 * Invece che un array di interi, il vector clock e' una mappa da nickname di un 
	 * processo (ovvero di un transceiver) ad un valore intero.
	 */
	private TreeMap<String, Integer> VC;
	
	private String VC2string;
	
	/**
	 * Crea un vector clock VUOTO
	 */
	public VectorClock() {
		VC= new TreeMap<String, Integer>();
		VC2string= VC.toString();
	}
	
	/**
	 * Restituisce l'iteratore degli elementi del vector clock
	 * @return vedi descrizione
	 */
	public Iterator<Map.Entry<String, Integer>> iterator() {
		return VC.entrySet().iterator();
	}

	public synchronized void addProcessClock(String processName) {
		VC.put(processName, 0);
		VC2string= VC.toString();
	}
	
	public synchronized void delProcessClock(String processName) {
		VC.remove(processName);
		VC2string= VC.toString();
	}
	
	public synchronized void incrementClock(String processName) {
		int val= VC.get(processName);
		val++;
		VC.put(processName, val);
		VC2string= VC.toString();
	}

	public synchronized void setClock(String processName, int timestamp) {
		VC.put(processName, timestamp);
		VC2string= VC.toString();
	}
	
	public synchronized boolean contains(String processName) {
		return VC.containsKey(processName);
	}
	
	public synchronized int getClock(String processName) {
		return VC.get(processName);
	}
	
	/**
	 * Restituiscve il numero di elementi del vector clock (numero di processi)
	 * @return vedi descrizione
	 */
	public synchronized int getDimension() {
		return VC.size();
	}
	
	public synchronized void clear(){
		
		VC.clear();
		VC2string= VC.toString();
	}

	@Override
	public synchronized boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o instanceof String) {
            VectorClock ovc = (VectorClock) o;
            if (this.VC.size() == ovc.VC.size()) {
            	// VC[k] == VC'[k] deve valere per ogni k
            	for (Map.Entry<String, Integer> entry : VC.entrySet()) {
            		int val1= entry.getValue().intValue();
            		int val2= ovc.VC.get(entry.getKey()).intValue();
            		if (val1 != val2) return false;
            	}
            	return true;
            } else {
            	// lunghezza diversa
            	return false;
            }
        }
        return false;
	}
	
	/**
	 * Restituisce true se this.VC <= c.VC
	 * Vedi libro per la semantica di <= per i vector clock
	 * @param c vector clock da confrontare
	 * @return vedi descrizione
	 */
	public boolean lessEquals(VectorClock c) {
		if (this.VC.size() == c.VC.size()) {
    		// VC[k] <= VC'[k] deve valere per ogni k
			for (Map.Entry<String, Integer> entry : VC.entrySet()) {
        		int val1= entry.getValue().intValue();
        		int val2= c.VC.get(entry.getKey()).intValue();
        		if (val1 > val2) return false;
        	}
        	return true;
        } else {
        	// lunghezza diversa
        	return false;
        }
	}
	

	/**
	 * Restituisce true se this.VC < c.VC
	 * Vedi libro per la semantica di < per i vector clock
	 * @param c vector clock da confrontare
	 * @return vedi descrizione
	 */
	public boolean less(VectorClock c) {
		return lessEquals(c) && !equals(c);
	}
	
	/**
	 * Restituisce true se this e c non sono confrontabili, ovvero
	 * sono vector clock di eventi paralleli.
	 * Vedi libro per la semantica di eventi paralleli
	 * @param c vector clock da confrontare
	 * @return vedi descrizione
	 */
	public boolean notComparable(VectorClock c) {
		return !this.lessEquals(c) && !c.lessEquals(this);
	}
	
	@Override
	public String toString() {
		return VC2string;
	}
	
	@Override
	public Object clone() {
        VectorClock clone = null;
        try {
            clone = (VectorClock) super.clone();
            
            clone.VC= new TreeMap<String, Integer>();
            for (Map.Entry<String, Integer> entry : VC.entrySet()) {
            	int val= entry.getValue();
            	clone.VC.put(entry.getKey(), val);
            }
            clone.VC2string= clone.VC.toString();
        } catch (CloneNotSupportedException e) {
            // non si puo' verificare
        	e.printStackTrace();
        }

        return clone;
    }
	
}
